1use crate::ext::io::*;
2use anyhow::Result;
3
4#[derive(Debug, Clone, Copy, PartialEq, Eq)]
5pub enum Oper {
6 B,
8 I,
10 A,
12 S,
14 T,
16}
17
18use Oper::*;
19
20pub struct Opcodes {
21 pub r#yield: u8,
22 pub add: u8,
23 pub escape_sequence: u8,
24 pub message1: u8,
25 pub message2: u8,
26 pub push_int: u8,
27 pub push_string: u8,
28 pub syscall: u8,
29 pub line_number: u8,
30 pub nop1: u8,
31 pub nop2: u8,
32 pub is_message1_obfuscated: bool,
33}
34
35pub struct Syscalls {
36 pub exec: i32,
37 pub exec_set_character_name: i32,
38}
39
40#[derive(Debug, Clone, Copy, PartialEq, Eq)]
41pub enum SlikyStringType {
42 Internal,
43 Message,
44 Name,
45}
46
47#[derive(Debug, Clone)]
48pub struct SlikyString {
49 pub start: u64,
50 pub len: u64,
51 pub typ: SlikyStringType,
52}
53
54#[derive(Debug, Clone)]
55pub enum Obj {
56 Byte(u8),
57 Int(i32),
58 Str(SlikyString),
59}
60
61pub trait Disasm: std::fmt::Debug {
62 fn stream(&self) -> &MemReader;
63 fn stream_mut(&mut self) -> &mut MemReader;
64 fn opcodes(&self) -> &'static Opcodes;
65 fn operands(&self) -> &'static [(u8, &'static [Oper])];
66 fn syscalls(&self) -> &'static [Syscalls];
67 fn code_offset(&self) -> u32;
68 fn big_endian_addresses(&self) -> &[u32];
69 fn push_big_endian_addresses(&mut self, addr: u32);
70 fn little_endian_addresses(&self) -> &[u32];
71 fn read_header(&mut self) -> Result<()>;
72 fn read_instruction(&mut self) -> Result<(u8, Vec<Obj>)> {
73 let opcode = self.stream_mut().read_u8()?;
74 let mut operands = Vec::new();
75 if let Some((_, ops)) = self.operands().iter().find(|(op, _)| *op == opcode) {
76 for &oper in *ops {
77 operands.push(self.read_operand(oper)?);
78 }
79 }
80 Ok((opcode, operands))
81 }
82 fn read_operand(&mut self, oper: Oper) -> Result<Obj> {
83 match oper {
84 B => Ok(Obj::Byte(self.stream_mut().read_u8()?)),
85 I => Ok(Obj::Int(self.stream_mut().read_i32_be()?)),
86 A => {
87 self.push_big_endian_addresses(self.stream().pos as u32);
88 Ok(Obj::Int(self.stream_mut().read_i32_be()?))
89 }
90 S | T => {
91 let start = self.stream().pos as u64;
92 let s = self.stream_mut().read_cstring()?;
93 Ok(Obj::Str(SlikyString {
94 start,
95 len: s.as_bytes_with_nul().len() as u64,
96 typ: SlikyStringType::Internal,
97 }))
98 }
99 }
100 }
101 fn read_code(&mut self) -> Result<Vec<SlikyString>> {
102 let mut stack: Vec<Obj> = Vec::new();
103 let mut message_start_offset = None;
104 let mut in_ruby = false;
105 let mut texts = Vec::new();
106 self.stream_mut().pos = self.code_offset() as usize;
107 while !self.stream().is_eof() {
108 let instr_offset = self.stream().pos as u64;
109 let (opcode, operands) = self.read_instruction()?;
110 let opcodes = self.opcodes();
112 if opcode == opcodes.message1 || opcode == opcodes.message2 {
113 if message_start_offset.is_none() {
114 message_start_offset = Some(instr_offset);
115 }
116 } else if opcode == opcodes.escape_sequence {
117 if let Some(Obj::Byte(b)) = operands.get(0) {
118 if *b == 0x1 {
119 in_ruby = true;
120 }
121 }
122 } else if opcode == opcodes.r#yield && in_ruby {
123 in_ruby = false;
124 } else if opcode == opcodes.push_int
125 && self.stream().cpeek_u8_at(instr_offset + 5)? == opcodes.line_number
126 {
127 } else if opcode == opcodes.line_number
129 || opcode == opcodes.nop1
130 || opcode == opcodes.nop2
131 {
132 } else {
134 if let Some(start) = message_start_offset {
135 let start = start as u64;
136 let text = SlikyString {
137 start,
138 len: instr_offset - start,
139 typ: SlikyStringType::Message,
140 };
141 texts.push(text);
142 }
143 message_start_offset = None;
144 in_ruby = false;
145 }
146 if opcode == opcodes.push_int || opcode == opcodes.push_string {
148 if !operands.is_empty() {
149 stack.push(operands[0].clone());
150 }
151 } else if opcode == opcodes.add && stack.len() >= 2 {
152 let value1 = stack.pop().unwrap();
153 let value2 = stack.pop().unwrap();
154 if let (Obj::Int(i1), Obj::Int(i2)) = (value1, value2) {
155 stack.push(Obj::Int(i1 + i2));
156 }
157 } else if opcode == opcodes.syscall && stack.len() >= 3 {
158 let func_id = stack.pop().unwrap();
159 let exec_id = stack.pop().unwrap();
160 let name = stack.pop().unwrap();
161 if let (Obj::Int(func_id), Obj::Int(exec_id), Obj::Str(name)) =
162 (func_id, exec_id, name)
163 {
164 for syscall in self.syscalls() {
165 if func_id == syscall.exec && exec_id == syscall.exec_set_character_name {
166 texts.push(SlikyString {
167 start: name.start - 1,
168 len: name.len + 1,
169 typ: SlikyStringType::Name,
170 });
171 }
172 }
173 }
174 stack.clear();
175 } else {
176 stack.clear();
177 }
178 }
179 Ok(texts)
180 }
181}
182
183pub const PLUS_OPCODES: Opcodes = Opcodes {
184 r#yield: 0x00,
185 add: 0x34,
186 escape_sequence: 0x1c,
187 message1: 0x0a,
188 message2: 0x0b,
189 push_int: 0x32,
190 push_string: 0x33,
191 syscall: 0x18,
192 line_number: 0xff,
193 nop1: 0xfc,
194 nop2: 0xfd,
195 is_message1_obfuscated: true,
196};
197
198const PLUS_OPERANDS: [(u8, &[Oper]); 53] = [
199 (0x00, &[]), (0x01, &[]), (0x02, &[]), (0x03, &[]), (0x04, &[]), (0x05, &[]), (0x06, &[]), (0x07, &[]), (0x08, &[]), (0x09, &[]), (0x0A, &[S]), (0x0B, &[T]), (0x0C, &[]), (0x0D, &[]), (0x0E, &[]), (0x0F, &[]), (0x10, &[]), (0x11, &[]), (0x12, &[]), (0x13, &[]), (0x14, &[A]), (0x15, &[A]), (0x16, &[A]), (0x17, &[]), (0x18, &[]), (0x19, &[I]), (0x1A, &[I]), (0x1B, &[A]), (0x1C, &[B]), (0x32, &[I]), (0x33, &[S]), (0x34, &[]), (0x35, &[]), (0x36, &[]), (0x37, &[]), (0x38, &[]), (0x39, &[]), (0x3A, &[]), (0x3B, &[]), (0x3C, &[]), (0x3D, &[]), (0x3E, &[]), (0x3F, &[]), (0x40, &[]), (0x41, &[]), (0x42, &[]), (0x43, &[]), (0xFA, &[]),
247 (0xFB, &[]),
248 (0xFC, &[]),
249 (0xFD, &[]),
250 (0xFE, &[]),
251 (0xFF, &[]),
252];
253
254const PLUS_SYSCALLS: [Syscalls; 2] = [
255 Syscalls {
256 exec: 29,
257 exec_set_character_name: 11,
258 },
259 Syscalls {
260 exec: 29,
261 exec_set_character_name: 15,
262 },
263];
264
265#[derive(Debug)]
266pub struct PlusDisasm {
267 stream: MemReader,
268 num_messages: u32,
269 num_special_messages: u32,
270 code_offset: u32,
271 big_endian_addresses: Vec<u32>,
272 little_endian_addresses: Vec<u32>,
273}
274
275impl PlusDisasm {
276 pub fn new(mut stream: MemReader) -> Result<Self> {
277 let num_messages = stream.read_u32()?;
278 let num_special_messages = stream.read_u32()?;
279 let code_offset = 8 + (num_messages + num_special_messages) * 4;
280 Ok(Self {
281 stream,
282 num_messages,
283 num_special_messages,
284 code_offset,
285 big_endian_addresses: Vec::new(),
286 little_endian_addresses: Vec::new(),
287 })
288 }
289}
290
291impl Disasm for PlusDisasm {
292 fn stream(&self) -> &MemReader {
293 &self.stream
294 }
295 fn stream_mut(&mut self) -> &mut MemReader {
296 &mut self.stream
297 }
298 fn opcodes(&self) -> &'static Opcodes {
299 &PLUS_OPCODES
300 }
301 fn operands(&self) -> &'static [(u8, &'static [Oper])] {
302 &PLUS_OPERANDS
303 }
304 fn syscalls(&self) -> &'static [Syscalls] {
305 &PLUS_SYSCALLS
306 }
307 fn code_offset(&self) -> u32 {
308 self.code_offset
309 }
310 fn big_endian_addresses(&self) -> &[u32] {
311 &self.big_endian_addresses
312 }
313 fn push_big_endian_addresses(&mut self, addr: u32) {
314 self.big_endian_addresses.push(addr);
315 }
316 fn little_endian_addresses(&self) -> &[u32] {
317 &self.little_endian_addresses
318 }
319 fn read_header(&mut self) -> Result<()> {
320 for i in 0..self.num_messages + self.num_special_messages {
321 self.little_endian_addresses.push(8 + i * 4);
322 }
323 self.stream.pos = self.code_offset as usize;
324 Ok(())
325 }
326}
327
328const AI6_WIN_OPCODES: Opcodes = Opcodes {
329 r#yield: 0x00,
330 add: 0x34,
331 escape_sequence: 0x1b,
332 message1: 0x0a,
333 message2: 0x0b,
334 push_int: 0x32,
335 push_string: 0x33,
336 syscall: 0x18,
337 line_number: 0xff,
338 nop1: 0xfc,
339 nop2: 0xfd,
340 is_message1_obfuscated: false,
341};
342
343const AI6_WIN_OPERANDS: [(u8, &[Oper]); 48] = [
344 (0x00, &[]), (0x01, &[]), (0x02, &[]), (0x03, &[]), (0x04, &[]), (0x05, &[]), (0x06, &[]), (0x07, &[]), (0x08, &[]), (0x09, &[]), (0x0A, &[S]), (0x0B, &[S]), (0x0C, &[]), (0x0D, &[]), (0x0E, &[]), (0x0F, &[]), (0x10, &[]), (0x11, &[]), (0x12, &[]), (0x13, &[]), (0x14, &[A]), (0x15, &[A]), (0x16, &[A]), (0x17, &[]), (0x18, &[]), (0x19, &[I]), (0x1A, &[A]), (0x1B, &[B]), (0x32, &[I]), (0x33, &[S]), (0x34, &[]), (0x35, &[]), (0x36, &[]), (0x37, &[]), (0x38, &[]), (0x39, &[]), (0x3A, &[]), (0x3B, &[]), (0x3C, &[]), (0x3D, &[]), (0x3E, &[]), (0x3F, &[]), (0x40, &[]), (0x41, &[]), (0x42, &[]), (0x43, &[]), (0xFE, &[]),
391 (0xFF, &[]),
392];
393
394const AI6_WIN_SYSCALLS: [Syscalls; 1] = [Syscalls {
395 exec: 31,
396 exec_set_character_name: 15,
397}];
398
399#[derive(Debug)]
400pub struct Ai6WinDisasm {
401 stream: MemReader,
402 num_messages: u32,
403 code_offset: u32,
404 big_endian_addresses: Vec<u32>,
405 little_endian_addresses: Vec<u32>,
406}
407
408impl Ai6WinDisasm {
409 pub fn new(mut stream: MemReader) -> Result<Self> {
410 let num_messages = stream.read_u32()?;
411 let code_offset = 4 + num_messages * 4;
412 Ok(Self {
413 stream,
414 num_messages,
415 code_offset,
416 big_endian_addresses: Vec::new(),
417 little_endian_addresses: Vec::new(),
418 })
419 }
420}
421
422impl Disasm for Ai6WinDisasm {
423 fn stream(&self) -> &MemReader {
424 &self.stream
425 }
426 fn stream_mut(&mut self) -> &mut MemReader {
427 &mut self.stream
428 }
429 fn opcodes(&self) -> &'static Opcodes {
430 &AI6_WIN_OPCODES
431 }
432 fn operands(&self) -> &'static [(u8, &'static [Oper])] {
433 &AI6_WIN_OPERANDS
434 }
435 fn syscalls(&self) -> &'static [Syscalls] {
436 &AI6_WIN_SYSCALLS
437 }
438 fn code_offset(&self) -> u32 {
439 self.code_offset
440 }
441 fn big_endian_addresses(&self) -> &[u32] {
442 &self.big_endian_addresses
443 }
444 fn push_big_endian_addresses(&mut self, addr: u32) {
445 self.big_endian_addresses.push(addr);
446 }
447 fn little_endian_addresses(&self) -> &[u32] {
448 &self.little_endian_addresses
449 }
450 fn read_header(&mut self) -> Result<()> {
451 for i in 0..self.num_messages {
452 self.little_endian_addresses.push(4 + i * 4);
453 }
454 self.stream.pos = self.code_offset as usize;
455 Ok(())
456 }
457}